export class StandardDate {
  localDate: Date;

  constructor(date?: Date | string | number) {
    if (date) {
      if (date instanceof Date) {
        this.localDate = date;
      } else if (typeof date === 'string' || typeof date === 'number') {
        // eslint-disable-next-line no-console
        console.warn('The string type is not recommended for date-picker, use "Date" type');
        this.localDate = new Date(date);
      } else {
        throw new Error('The input date type is not supported ("Date" is now recommended)');
      }
    } else {
      this.localDate = null;
    }
  }

  getDay(): number {
    return this.localDate.getDay();
  }

  getTime(): number {
    return this.localDate.getTime();
  }

  getDate(): number {
    return this.localDate.getDate();
  }

  getMonth(): number {
    return this.localDate.getMonth();
  }

  getHours(): number {
    return this.localDate.getHours();
  }

  getYear(): number {
    return this.localDate.getFullYear();
  }

  getMinutes(): number {
    return this.localDate.getMinutes();
  }

  getSeconds(): number {
    return this.localDate.getSeconds();
  }

  getMilliseconds(): number {
    return this.localDate.getMilliseconds();
  }

  setDate(dates): Date {
    return new Date(this.localDate.setDate(dates));
  }

  setHours(hours): Date {
    return new Date(this.localDate.setHours(hours));
  }

  setYear(years): Date {
    return new Date(this.localDate.setFullYear(years));
  }

  setMonth(months): Date {
    return new Date(this.localDate.setMonth(months));
  }

  clone(): StandardDate {
    return new StandardDate(new Date(this.localDate));
  }

  getSundayIndex(weekStart): number {
    return weekStart > 0 ? 7 - weekStart : 0;
  }

  setMinutes(minutes): Date {
    return new Date(this.localDate.setMinutes(minutes));
  }

  setSeconds(seconds): Date {
    return new Date(this.localDate.setSeconds(seconds));
  }

  isSameYear(date): boolean {
    return date.localDate.getFullYear() === this.localDate.getFullYear();
  }

  setMilliseconds(milliseconds): Date {
    return new Date(this.localDate.setMilliseconds(milliseconds));
  }

  isToday(): boolean {
    const today = new Date().toDateString();
    const day = this.localDate.toDateString();
    return day === today;
  }

  calendarStart(weekStart): StandardDate {
    return new StandardDate(this.startDateOfWeek(this.startWeekOfMonth(this.localDate, weekStart)));
  }

  addDays(num: number): Date {
    const newDate = new Date(this.clone().localDate);
    return new Date(newDate.setDate(newDate.getDate() + num));
  }

  startDateOfWeek(d: { date: Date; weekIndex: number }): Date {
    const date = d.date.setDate(d.date.getDate() - d.weekIndex);
    return new Date(date);
  }

  addYears(num: number): Date {
    const newDate = new Date(this.clone().localDate);
    return new Date(newDate.setFullYear(newDate.getFullYear() + num));
  }

  isSameDay(date): boolean {
    const currentDay = date.localDate.toDateString();
    const day = this.localDate.toDateString();
    return day === currentDay;
  }

  isSameMonth(date): boolean {
    const sameYear = date.localDate.getFullYear() === this.localDate.getFullYear() ? true : false;
    const sameMonth = date.localDate.getMonth() === this.localDate.getMonth() ? true : false;
    return sameYear && sameMonth;
  }

  startWeekOfMonth(date, weekStart): { date: Date; weekIndex: number } {
    const day = new Date();
    day.setDate(1);
    day.setMonth(date.getMonth());
    day.setFullYear(date.getFullYear());

    const dayIndex = day.getDay() + this.getSundayIndex(weekStart);
    return { date: day, weekIndex: dayIndex >= 7 ? dayIndex - 7 : dayIndex };
  }

  addMonths(num: number): Date {
    const newDate = new Date(this.clone().localDate);
    const lastDayofNextMonth = new Date(newDate.getFullYear(), newDate.getMonth() + num + 1, 0).getDate();
    // If the number of days in the target month is less than the date of the current month,
    // the date is updated to the last day of the target month.
    if (lastDayofNextMonth < this.getDate()) {
      newDate.setDate(lastDayofNextMonth);
    }
    return new Date(newDate.setMonth(newDate.getMonth() + num));
  }

  isCompareDay(date, type: 'before' | 'after' | 'match'): boolean {
    const source = new StandardDate(
      new StandardDate(
        new StandardDate(new StandardDate(this.clone().setHours(0)).setMinutes(0)).setSeconds(0),
      ).setMilliseconds(0),
    );
    const target = new StandardDate(
      new StandardDate(
        new StandardDate(new StandardDate(date.clone().setHours(0)).setMinutes(0)).setSeconds(0),
      ).setMilliseconds(0),
    );

    if (type === 'before') {
      return source.localDate.getTime() < target.localDate.getTime();
    } else if (type === 'after') {
      return source.localDate.getTime() > target.localDate.getTime();
    } else {
      return source.localDate.getTime() === target.localDate.getTime();
    }
  }

  isCompareMonth(date, type: 'less-than' | 'more-than' | 'equal'): boolean {
    const source = new StandardDate(
      new StandardDate(
        new StandardDate(
          new StandardDate(new StandardDate(this.clone().setDate(1)).setHours(0)).setMinutes(0),
        ).setSeconds(0),
      ).setMilliseconds(0),
    );
    const target = new StandardDate(
      new StandardDate(
        new StandardDate(
          new StandardDate(new StandardDate(date.clone().setDate(1)).setHours(0)).setMinutes(0),
        ).setSeconds(0),
      ).setMilliseconds(0),
    );

    if (type === 'less-than') {
      return source.localDate.getTime() < target.localDate.getTime();
    } else if (type === 'more-than') {
      return source.localDate.getTime() > target.localDate.getTime();
    } else {
      return source.localDate.getTime() === target.localDate.getTime();
    }
  }
}
